﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;


using GMap.NET;
using GMap.NET.MapProviders;
using GMap.NET.WindowsPresentation;
using System.Windows;
using System.Windows.Shapes;
using System.Windows.Media;
using GMap.NET.Projections;

using RZD_maps.Modules.Map.Model;
using RZD_maps.Infrastructure;
using Catfood.Shapefile;
using Microsoft.Practices.Prism.Events;

using System.Diagnostics;
using RZD_maps.Modules.Map.CustomMarkers;
using RZD_maps.Modules.Map.Views;
using StaffLibrary;
using System.ComponentModel;
using System.Threading;
using System.Windows.Threading;

namespace RZD_maps.Modules.Map.ViewModel
{
    public class VMMap
    {
        private MapModel model;

        private TransportModel transportModel;
        private bool LinesVisible = true;//true; // признак видимости ж/д линий
        private bool TransportVisible = true;//true; // признак видимости ж/д составов
        private bool StationsVisible = true;//true; // признак видимости ж/д станций
        private bool MarkersVisible = true;//true; // признак видимости маркеров

        private IEventAggregator eventAggregator;
        private SubscriptionToken subscriptionToken;
        private SubscriptionToken subscriptionTokenStation;


        public VMMap()
        {
            model = new MapModel();
            transportModel = new TransportModel();
        }


        public VMMap(IEventAggregator eventAggregator) : this()
        {
            this.eventAggregator = eventAggregator;
            SubscriptCommands();
        }

        public void ControlDataEventHandler(ControlDataItem controlDataItem)
        {
            Debug.Assert(View != null);
            bool bNeedChangeLayers = false;
            switch (controlDataItem.command)
            {
                case ControlDataCommandEnum.bing:
                    if (GMapProviders.BingMap != null)
                        view.GetMapControl().MapProvider = GMapProviders.BingMap;
                    break;
                case ControlDataCommandEnum.osm:
                    if (OpenCycleTransportMapProvider.Instance != null)
                        view.GetMapControl().MapProvider = GMapProviders.BingSatelliteMap;
                    //view.GetMapControl().MapProvider = OpenCycleTransportMapProvider.Instance ;//GMapProviders.OpenStreetMap;
                    break;
                case ControlDataCommandEnum.line:
                    bNeedChangeLayers = true;
                    LinesVisible = !LinesVisible;
                    break;
                case ControlDataCommandEnum.markers:
                    bNeedChangeLayers = true;
                    MarkersVisible = !MarkersVisible;
                    break;
                case ControlDataCommandEnum.plain:
                    view.GetMapControl().MapProvider = GMapProviders.EmptyProvider;
                    break;
                case ControlDataCommandEnum.scale10:
                    view.GetMapControl().Zoom = 10;
                    break;
                case ControlDataCommandEnum.scale100:
                    view.GetMapControl().Zoom = 5;
                    break;
                case ControlDataCommandEnum.station:
                    bNeedChangeLayers = true;
                    StationsVisible = !StationsVisible;
                    break;
                case ControlDataCommandEnum.vehicle:
                    bNeedChangeLayers = true;
                    TransportVisible = !TransportVisible;
                    break;
            }
            if (bNeedChangeLayers)
                DisplayLayers();
        }

        public bool ControlDataFilter(ControlDataItem controlData)
        {
            return true;
        }

        public void SubscriptCommands()
        {
            ControlDataEvent evnt = eventAggregator.GetEvent<ControlDataEvent>();
            MapPositionItemEvent evntStation = eventAggregator.GetEvent<MapPositionItemEvent>();
            if (subscriptionToken != null)
            {
                evnt.Unsubscribe(subscriptionToken);
            }

            if (subscriptionTokenStation != null)
            {
                evnt.Unsubscribe(subscriptionTokenStation);
            }

            subscriptionToken = evnt.Subscribe(ControlDataEventHandler, ThreadOption.UIThread, false, ControlDataFilter);
            subscriptionTokenStation = evntStation.Subscribe(MapPositionItemEventHandler, ThreadOption.UIThread, false, MapPositionItemFilter);
        }

        private IMapsView view;

        public void MapPositionItemEventHandler(MapPositionItem mapPositionItem)
        {
            Debug.Assert(View != null);
            View.GetMapControl().Position = new PointLatLng(mapPositionItem.lat, mapPositionItem.lng);
            //View.GetMapControl().Zoom = mapPositionItem.zoom;
        }

        public bool MapPositionItemFilter(MapPositionItem controlData)
        {
            return true;
        }

        public IMapsView View { get {return view;}
            set
            {
                view = value;
                //LoadShapeFile(@"railway-line.shp", view.GetMapControl());
                //LoadShapeFile.LoadShapeFileStations(@"railway-station-point.shp", view.GetMapControl());
                //LoadMarkersLayer();
                //SartTransport();
                StartTransport();
                DisplayLayers();
                GMapControl c = view.GetMapControl();
                c.ShowCenter = false;
                c.MouseWheelZoomType = MouseWheelZoomType.MousePositionWithoutCenter; //MouseWheelZoomType.MousePositionAndCenter;
               
            }
        }

        public void MapLayerEventHandler(ControlDataItem mapLayer)
        {
            Debug.Assert(View != null);
        }

        public bool MapLayerFilter(ControlDataItem mapLayer)
        {
            return true;
        }

        private int layerInfo;

        public int LayerInfo
        {
            get
            {
                return layerInfo;
            }
            set
            {
                layerInfo = value;
                ControlDataEvent mapLayerEvent = eventAggregator.GetEvent<ControlDataEvent>();
                if (subscriptionToken != null)
                {
                    mapLayerEvent.Unsubscribe(subscriptionToken);
                }

                subscriptionToken = mapLayerEvent.Subscribe(MapLayerEventHandler, ThreadOption.UIThread, false, MapLayerFilter);
            }
        }

        private struct routeData
        {
            public PointLatLng point;
            public double velocity;
        }

        private void DisplayRoute(IEnumerable<BaseCategory> items)
        {
            double ESRIShapepolylat;
            double ESRIShapepolylong;
            double velocity1 = 0.0;
            double velocity2 = 0.0;
            List<PointLatLng> routePoints = new List<PointLatLng>();

            List<routeData> routePointsdt = new List<routeData>();

//            View.GetMapControl().Markers.Clear();
            foreach (var item in items)
            {
                ESRIShapepolylat = item.Lat;
                ESRIShapepolylong = item.Lng;
                routePointsdt.Add(new routeData() { point = new PointLatLng(ESRIShapepolylat, ESRIShapepolylong), velocity = item.Velocity });
            }
            if (routePointsdt.Count < 2) return;
            int i = 0;
            bool bNeedMarker = false;
            bool bFirst = true;
            while (i < routePointsdt.Count - 1)
            {
                routePoints.Add(routePointsdt[i].point);
                routePoints.Add(routePointsdt[i + 1].point);
                i += 1;
                MapRoute r = new MapRoute(routePoints, "");
                GMapMarker m = new GMapMarker(routePoints[0]);
                m.Route.AddRange(r.Points);
                m.RegenerateRouteShape(View.GetMapControl());
                (m.Shape as System.Windows.Shapes.Path).Opacity = 1.0;
                Brush brsh = System.Windows.Media.Brushes.Green;

                bNeedMarker = false;
                if (bFirst == true)
                {
                    velocity1 = routePointsdt[i].velocity;
                    velocity2 = routePointsdt[i].velocity;
                    bNeedMarker = true;
                    bFirst = false;
                }
                else
                {
                    velocity1 = velocity2;
                    velocity2 = routePointsdt[i].velocity;
                    if (velocity2 != velocity1)
                        bNeedMarker = true;
                }

                if (routePointsdt[i].velocity < 30)
                    brsh = System.Windows.Media.Brushes.Red;
                else if (routePointsdt[i].velocity < 50)
                    brsh = System.Windows.Media.Brushes.Orange;
                else if (routePointsdt[i].velocity < 60)
                    brsh = System.Windows.Media.Brushes.Yellow;
                else
                    brsh = System.Windows.Media.Brushes.Green; // new SolidColorBrush(Color.FromRgb(255, 0, 0));
                (m.Shape as System.Windows.Shapes.Path).Stroke = brsh;

                (m.Shape as System.Windows.Shapes.Path).StrokeThickness = 2;

                View.GetMapControl().Markers.Add(m);
                if (bNeedMarker)
                {
                    GMapMarker marker = new GMapMarker(routePoints[0]);
                    marker.ZIndex = 50;
                    //var v = new CircleVisual(marker, Brushes.Red);
                    var v = new CircleVelocity(marker, brsh);
                    v.Text = velocity2.ToString();
                    v.Angle = 180;//0;
                    v.Stroke = new Pen(Brushes.Gray, 2.0);
                    marker.Shape = v;
                    View.GetMapControl().Markers.Add(marker);
                }
                routePoints.Clear();
            }

        }

        private void DisplayLayers()
        {
            
            View.GetMapControl().Markers.Clear();
            if (LinesVisible)
                LoadShapeFile(@"railway-line.shp", view.GetMapControl());
            if (TransportVisible)
            {
                if (!transport.IsBusy)
                {
                    transport.RunWorkerAsync();
                }
                else
                {
                    lock (train)
                    {
                        train.Clear();
                        trainMarkers.Clear();
                    }
                }
                foreach(var item in transportModel.Categories)
                    DisplayRoute(item.SubCategories);
            }
            else
            {
                if (transport.IsBusy)
                {
                    transport.CancelAsync();
                }
            }
            if (MarkersVisible)
            {
                
                LoadMarkersLayer();
            }
            if (StationsVisible)
                LoadShapeFileStations(@"railway-station-point.shp", view.GetMapControl());
            
            
        }

        public static GMapProvider MapProvider = MapModel.MapProvider;
        public static PointLatLng Position = new PointLatLng(55.8062181, 38.9791242);//new PointLatLng(54.415238, 40.359397);//new PointLatLng(55.8062181, 38.9791242);

        public UIElement Shape;

        BackgroundWorker transport = new BackgroundWorker();

        public string DecodeFromUtf8(string utf8String)
        {
            // Get UTF8 bytes by reading each byte with ANSI encoding
            byte[] utf8Bytes = Encoding.Default.GetBytes(utf8String);

            // Convert UTF8 bytes to UTF16 bytes
            byte[] utf16Bytes = Encoding.Convert(Encoding.UTF8, Encoding.Unicode, utf8Bytes);

            // Return UTF16 bytes as UTF16 string
            string good = Encoding.Unicode.GetString(utf16Bytes);

            return good;

        }

        public void StartTransport()
        {
            Stuff.InitDataFromXML();
            transport.DoWork += new DoWorkEventHandler(transport_DoWork);
            transport.ProgressChanged += new ProgressChangedEventHandler(transport_ProgressChanged);
            transport.WorkerSupportsCancellation = true;
            transport.WorkerReportsProgress = true;
            transport.RunWorkerCompleted += new RunWorkerCompletedEventHandler(transport_RunWorkerCompleted);
            //transport.RunWorkerAsync();

        }

        //private bool bTransportWorkCompleted = false;

        void transport_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            //bTransportWorkCompleted = true;
        }

        readonly List<VehicleData> train = new List<VehicleData>();
        readonly Dictionary<int, GMapMarker> trainMarkers = new Dictionary<int, GMapMarker>();

        bool firstLoadTrasport = true;

        

        void transport_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            //return;
            List<TrainPositonData> list_trn = new List<TrainPositonData>();    
            using (((MapsView)View).Dispatcher.DisableProcessing())
            {
                lock (train)
                {
                    
                    foreach (VehicleData d in train)
                    {
                        GMapMarker marker;

                        if (!trainMarkers.TryGetValue(d.Id, out marker))
                        {
                            marker = new GMapMarker(new PointLatLng(d.Lat, d.Lng));
                            marker.Tag = d.Id;

                            var v = new RectangleVisual(marker, Brushes.Blue);//CircleVisual(marker, Brushes.Blue);
                            {
                                v.Stroke = new Pen(Brushes.Gray, 2.0);
                            }
                            marker.Shape = v;

                            trainMarkers[d.Id] = marker;
                            View.GetMapControl().Markers.Add(marker);
                        }
                        else
                        {
                            marker.Position = new PointLatLng(d.Lat, d.Lng);
                            //var shape = (marker.Shape as CircleVisual);
                            var shape = (marker.Shape as RectangleVisual);
                            {
                                shape.Text = d.Line;
                                shape.Angle = d.Bearing;
                                shape.Tooltip.SetValues("Train", d);

                                if (shape.IsChanged)
                                {
                                    shape.UpdateVisual(false);
                                }
                            }
                        }
                        TrainPositonData dt = new TrainPositonData();
                        dt.id = d.Id;
                        dt.lat = d.Lat;
                        dt.lng = d.Lng;
                        list_trn.Add(dt);
                    }

                    TrainPosition controlData = new TrainPosition();
                    controlData.list = list_trn;
                    
                    eventAggregator.GetEvent<TrainPositionEvent>().Publish(controlData);
                }

                if (firstLoadTrasport)
                {
                    firstLoadTrasport = false;
                }
            }
        }

        void transport_DoWork(object sender, DoWorkEventArgs e)
        {
            while (!transport.CancellationPending)
            {
                try
                {

                    lock (train)
                    {
                        //Stuff.GetTransportData(TransportType.Train, string.Empty, train);
                        train.Clear();
                        Stuff.GetTransportDataXML(TransportType.Train, string.Empty, train);
                    }

                    transport.ReportProgress(10);
                }
                catch (Exception ex)
                {
                    Debug.WriteLine("transport_DoWork: " + ex.ToString());
                }
                Thread.Sleep(500);
            }
            trainMarkers.Clear();
        }

        /// <summary>
        /// Загрузить информацию о маркерах
        /// </summary>
        public void LoadMarkersLayer()
        {
            GeoCoderStatusCode status = GeoCoderStatusCode.Unknow;
            List<PointAndInfo> objects = new List<PointAndInfo>();
            {
                string area = "Орехово-Зуево";
                PointLatLng? pos = GMapProviders.GoogleMap.GetPoint("Россия, Московская область, " + area, out status);
                if (pos != null && status == GeoCoderStatusCode.G_GEO_SUCCESS)
                {
                    objects.Add(new PointAndInfo(pos.Value, area));
                }
            }
            foreach (var o in objects)
            {
                GMapMarker it = new GMapMarker(o.Point);
                {
                    it.ZIndex = 55;
                    var s = new CustomMarkerDemo((MapsView)View, it, o.Info);
                    it.Shape = s;
                }

                View.GetMapControl().Markers.Add(it);
            }

        }

        /// <summary>
        /// Загрузить координаты железнодорожных станций и отобразить на карте
        /// </summary>
        /// <param name="ShapeMunicipality"></param>
        /// <param name="map"></param>
        public void LoadShapeFileStations(string ShapeMunicipality, GMapControl map)
        {
            Shapefile myshapefile = new Shapefile();
            ShapePoint mypoly;
            double ESRIShapepolylat;
            double ESRIShapepolylong;

            string ESRIMunName;



            myshapefile.Open(ShapeMunicipality);
            List<PointLatLng> routePoints = new List<PointLatLng>();

            string area = "Орехово-Зуево";
            GeoCoderStatusCode status = GeoCoderStatusCode.Unknow;
            PointLatLng? pos = new PointLatLng(55.8062181, 38.9791242); ;//GMapProviders.GoogleMap.GetPoint("Россия, Московская область, " + area, out status);
            double radius_area = 50;//8.8;


            foreach (var Shapes in myshapefile)
            {
                ESRIMunName = Shapes.GetMetadata("name");
                ESRIMunName = DecodeFromUtf8(ESRIMunName);
               // ESRIProvince = Shapes.GetMetadata("NAME_1");
               // ESRIProvince = Shapes.GetMetadata("NAME_2");

                mypoly = myshapefile.Current as ShapePoint;
                if (mypoly == null) continue;
                NumberFormatInfo provider = new NumberFormatInfo();
                provider.NumberDecimalSeparator = ".";
                provider.NumberGroupSeparator = ",";
                provider.NumberGroupSizes = new int[] { 3 };

                ESRIShapepolylat = mypoly.Point.Y;
                ESRIShapepolylong = mypoly.Point.X;

                PointLatLng pt = new PointLatLng(ESRIShapepolylat, ESRIShapepolylong);

                //if (map.MapProvider.Projection.GetDistance(pos.Value, pt) > radius_area) continue;
                if (MercatorProjection.Instance.GetDistance(pos.Value, pt) > radius_area) continue;
                GMapMarker marker = new GMapMarker(pt);
                marker.ZIndex = 50;
                var v = new CircleVisual(marker, Brushes.Red);
                v.Text = ESRIMunName;
                v.Angle = 0;
                VehicleData d = new VehicleData();
                d.AreaName = "";
                d.Bearing = 0;
                d.Id = 0;
                d.LastStop = "";
                d.Lat = ESRIShapepolylat;
                d.Lng = ESRIShapepolylong;
                d.StreetName = "";
                d.Time = "";
                d.TrackType = "";
                d.Line = ESRIMunName;
                v.Tooltip.SetValues("", d);
                v.Stroke = new Pen(Brushes.Gray, 2.0);
                marker.Shape = v;
                map.Markers.Add(marker);
            }
        }

        /// <summary>
        /// Загрузить координаты ж/д путей и отобразить на карте
        /// </summary>
        /// <param name="ShapeMunicipality"></param>
        /// <param name="map"></param>
        public void LoadShapeFile(string ShapeMunicipality, GMapControl map)
        {
            Shapefile myshapefile = new Shapefile();
            ShapePolyLine mypoly;
            double ESRIShapepolylat;
            double ESRIShapepolylong;

            //string ESRIProvince;
            //string ESRIMunName;
            //bool bFirst = true;

            //GPoint offset;
            //GPoint p;

            string area = "Орехово-Зуево";
           // GeoCoderStatusCode status = GeoCoderStatusCode.Unknow;
            //PointLatLng? pos = GMapProviders.GoogleMap.GetPoint("Россия, Московская область, " + area, out status);
            PointLatLng? pos = new PointLatLng(55.8062181, 38.9791242);//new PointLatLng(54.415238, 40.359397);// new PointLatLng(55.8062181, 38.9791242); 
            double radius_area = 50;//8.8;


            myshapefile.Open(ShapeMunicipality);
            List<PointLatLng> routePoints = new List<PointLatLng>();

            foreach (var Shapes in myshapefile)
            {
                mypoly = myshapefile.Current as ShapePolyLine;
                if (mypoly == null) continue;
                NumberFormatInfo provider = new NumberFormatInfo();
                provider.NumberDecimalSeparator = ".";
                provider.NumberGroupSeparator = ",";
                provider.NumberGroupSizes = new int[] { 3 };

                foreach (var part in mypoly.Parts)
                {
                    //if (part.Count() < 50) continue;
                    foreach(var item in part)
                    {
                        ESRIShapepolylat = item.Y;
                        ESRIShapepolylong = item.X;
                        //if (map.MapProvider.Projection.GetDistance(pos.Value, new PointLatLng(ESRIShapepolylat, ESRIShapepolylong)) > radius_area) continue;
                        //if (GMapProviders.OpenStreetMap.Projection.GetDistance(pos.Value, new PointLatLng(ESRIShapepolylat, ESRIShapepolylong)) > radius_area) continue;
                        if (MercatorProjection.Instance.GetDistance(pos.Value, new PointLatLng(ESRIShapepolylat, ESRIShapepolylong)) > radius_area) continue;
                        routePoints.Add(new PointLatLng(ESRIShapepolylat, ESRIShapepolylong));
                    }
                    if (routePoints.Count < 2) continue;
                    MapRoute r = new MapRoute(routePoints, "");
                    GMapMarker m = new GMapMarker(routePoints[0]);
                    m.Route.AddRange(r.Points);
                    m.RegenerateRouteShape(map);
                    (m.Shape as System.Windows.Shapes.Path).Opacity = 1.0;
                    (m.Shape as System.Windows.Shapes.Path).StrokeThickness = 2;// 0.5;
                    (m.Shape as System.Windows.Shapes.Path).Fill = System.Windows.Media.Brushes.Red;
                    (m.Shape as System.Windows.Shapes.Path).Stroke = System.Windows.Media.Brushes.Red;//new SolidColorBrush(Color.FromRgb(255, 0, 0)); //System.Windows.Media.Brushes.Red; // new SolidColorBrush(Color.FromRgb(255, 0, 0));
                    
                    //1;//4;
                    //m.RegenerateRouteShape(map);
                    //(m.Shape as System.Windows.Shapes.Path).Opacity = 1.0;
                    //(m.Shape as System.Windows.Shapes.Path).Stroke = new SolidColorBrush(Color.FromRgb(255,0,0));
                    //(m.Shape as System.Windows.Shapes.Path).StrokeThickness = 1;
                    ////m.RegenerateRouteShape(map);
                    //m.ZIndex = -1;
                    //m.RegenerateRouteShape(map);
                    map.Markers.Add(m);
                    routePoints.Clear();

                }

                

                
                
                //            (m.Shape as System.Windows.Shapes.Path).Stroke = new SolidColorBrush(Color.FromRgb(255,0,0));
                //            (m.Shape as System.Windows.Shapes.Path).StrokeThickness = 4;

                //            m.RegenerateRouteShape(map);
                //            m.ZIndex = -1;

                //map.Markers.Add(m);
                

            }
 //           MapRoute r = new MapRoute(routePoints, "РЖД");

            //GMapMarker m = new GMapMarker();
            //m.Route.AddRange(r.Points);
            //m.RegenerateRouteShape(map);
            //(m.Shape as System.Windows.Shapes.Path).Stroke = new SolidColorBrush(Color.FromRgb(255,0,0));
            //(m.Shape as System.Windows.Shapes.Path).StrokeThickness = 4;
            //m.RegenerateRouteShape(map);
            //m.ZIndex = -1;

            //map.Markers.Add(m);
            //routePoints.Clear();
        }
        //public void LoadShapeFile(string ShapeMunicipality, GMapControl map)
        //{
        //    Shapefile myshapefile = new Shapefile();
        //    ShapePoint mypoly;
        //    double ESRIShapepolylat;
        //    double ESRIShapepolylong;
            
        //    string ESRIProvince;
        //    string ESRIMunName;
        //    bool bFirst = true;

        //    GPoint offset;
        //    GPoint p;

        //    myshapefile.Open(ShapeMunicipality);
        //    var localPath = new List<System.Windows.Point>();

        //    foreach (var Shapes in myshapefile)
        //    {
        //        mypoly = myshapefile.Current as ShapePoint;
        //        if (mypoly == null) continue;
        //        NumberFormatInfo provider = new NumberFormatInfo();
        //        provider.NumberDecimalSeparator = ".";
        //        provider.NumberGroupSeparator = ",";
        //        provider.NumberGroupSizes = new int[] { 3 };


        //        ESRIShapepolylat = Convert.ToDouble(mypoly.Point.Y, provider);
        //        ESRIShapepolylong = Convert.ToDouble(mypoly.Point.X, provider);

        //        if (bFirst == true)
        //        {
        //            bFirst = false;
        //            offset = map.FromLatLngToLocal(new PointLatLng(ESRIShapepolylat, ESRIShapepolylong));
        //            p = offset;
        //            //localPath.Add(new System.Windows.Point(p.X - offset.X, p.Y - offset.Y));
        //            localPath.Add(new System.Windows.Point(p.X , p.Y));
        //            continue;
        //        }
        //        p = map.FromLatLngToLocal(new PointLatLng(ESRIShapepolylat, ESRIShapepolylong));
        //        offset = map.FromLatLngToLocal(new PointLatLng(ESRIShapepolylat, ESRIShapepolylong));
        //        ESRIMunName = mypoly.GetMetadata("NAME_3");
        //        ESRIProvince = mypoly.GetMetadata("NAME_1");
        //        ESRIProvince = mypoly.GetMetadata("NAME_2");

        //        //localPath.Add(new System.Windows.Point(p.X - offset.X, p.Y - offset.Y));
        //        localPath.Add(new System.Windows.Point(p.X, p.Y));
        //    }
        //    var shape = map.CreateRoutePath(localPath);
        //    GMapMarker currentMarker = new GMapMarker(map.Position);
        //    {
        //        currentMarker.Shape = shape;
        //        //currentMarker.Offset = new System.Windows.Point(-15, -15);
        //        currentMarker.ZIndex = int.MaxValue;
        //        map.Markers.Add(currentMarker);
        //    }

        //}

    }
}
